/**
 * @author:albert
 */

package com.bkbw.live.battle.net.client;

import java.lang.reflect.Constructor;
import java.lang.reflect.Type;
import java.net.InetSocketAddress;
import java.sql.Connection;
import java.util.HashMap;
import java.util.List;

import net.sf.json.JSONArray;

import org.apache.mina.core.RuntimeIoException;
import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.example.imagine.step1.ImageRequest;
import org.apache.mina.example.imagine.step1.ImageResponse;
import org.apache.mina.example.imagine.step1.client.ImageListener;
import org.apache.mina.example.imagine.step1.codec.ImageCodecFactory;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.transport.socket.SocketConnector;
import org.apache.mina.transport.socket.nio.NioSocketConnector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.bkbw.live.battle.database.DatabaseManager;
import com.bkbw.live.battle.database.bean.BallBean;
import com.bkbw.live.battle.database.bean.LevelExpBean;
import com.bkbw.live.battle.database.bean.TableTemplate;
import com.bkbw.live.battle.net.message.MessageCenter;
import com.bkbw.live.battle.net.message.MessageObject;
import com.bkbw.live.common.GameConfig;
import com.bkbw.live.common.dataproto.BattleToDatabasePb.BS2DBBagItemDetailReq;
import com.bkbw.live.common.dataproto.BattleToDatabasePb.BS2DBBuyItemReq;
import com.bkbw.live.common.dataproto.BattleToDatabasePb.BS2DBCharacterReq;
import com.bkbw.live.common.dataproto.BattleToDatabasePb.BS2DBDropBagItemReq;
import com.bkbw.live.common.dataproto.BattleToDatabasePb.BS2DBEquipReq;
import com.bkbw.live.common.dataproto.BattleToDatabasePb.BS2DBSaveCharacterReq;
import com.bkbw.live.common.dataproto.BattleToDatabasePb.BS2DBTableReq;
import com.bkbw.live.common.dataproto.BattleToDatabasePb.BS2DBUseBagItemReq;
import com.bkbw.live.common.dataproto.BattleToDatabasePb.DB2BSBuyItemResp;
import com.bkbw.live.common.dataproto.BattleToDatabasePb.DB2BSCharacterResp;
import com.bkbw.live.common.dataproto.BattleToDatabasePb.DB2BSDropBagItemResp;
import com.bkbw.live.common.dataproto.BattleToDatabasePb.DB2BSEquipResp;
import com.bkbw.live.common.dataproto.BattleToDatabasePb.DB2BSTableResp;
import com.bkbw.live.common.dataproto.BattleToDatabasePb.HeadType;
import com.bkbw.live.common.dataproto.CommonMessagePb.BagItemChangePb;
import com.bkbw.live.common.dataproto.CommonMessagePb.HashLongInt;
import com.bkbw.live.common.dataproto.CommonMessagePb.TripleLongIntIntPb;
import com.bkbw.live.common.net.codec.NetCodecFactory;
import com.bkbw.live.common.net.codec.RawMessageObject;
import com.bkbw.live.database.util.DatabasePool;
import com.bkbw.live.database.util.DatabaseUtil;

public class DatabaseClient extends IoHandlerAdapter {
	
//	private static DatabaseClient instance = new DatabaseClient(GameConfig.DatabaseServerIp, GameConfig.DatabaseServerPort);
//	public static DatabaseClient getInstance() {
//		return instance;
//	}
	
	Logger logger = LoggerFactory.getLogger(GameConfig.LoggerName);
	
	public static final int CONNECT_TIMEOUT = 3000;
	private String host;
	private int port;
	private SocketConnector connector;
	private IoSession session;
	
	private String testJson;
	public String getTestJson() {
		return testJson;
	}

	public DatabaseClient(String host, int port) {
		this.host = host;
		this.port = port;
		this.connector = new NioSocketConnector();
		this.connector.getFilterChain().addLast("codec",
				new ProtocolCodecFilter(new NetCodecFactory()));
		this.connector.setHandler(this);
	}

	public boolean isConnected() {
		return ((this.session != null) && (this.session.isConnected()));
	}

	public void connect() {
		ConnectFuture connectFuture = this.connector
				.connect(new InetSocketAddress(this.host, this.port));
		connectFuture.awaitUninterruptibly(3000L);
		try {
			this.session = connectFuture.getSession();
		} catch (RuntimeIoException e) {
		}
	}

	public void disconnect() {
		if (this.session != null) {
			this.session.close(true).awaitUninterruptibly(3000L);
			this.session = null;
		}
	}

	@Override
	public void sessionOpened(IoSession session) throws Exception {
		if (testJson == null) {
			logger.debug("session open request test");
			for (String table : DatabaseManager.getStaticTableNames()) {
				requestTable(table);
			}
//			requestTable(DatabaseManager.staticBallTable);
//			requestTable(DatabaseManager.staticLevelExpTable);
//			requestTable(DatabaseManager.staticRoleTable);
//			requestTable(DatabaseManager.staticVipTable);
//			requestTable(DatabaseManager.staticWeaponHotTable);
//			requestTable(DatabaseManager.staticWeaponTable);
		}
	}

	@Override
	public void sessionClosed(IoSession session) throws Exception {
		
	}
	
	public void requestTable(String tableName){
		BS2DBTableReq.Builder tableReqBuilder = BS2DBTableReq.newBuilder();
		logger.debug("request "+tableName);
		tableReqBuilder.setTableName(tableName);
		BS2DBTableReq tableReq = tableReqBuilder.build();
		RawMessageObject rawMessageObject = new RawMessageObject((short)HeadType.BS2DB_TABLE_REQ_VALUE, tableReq.toByteArray(), session);
		sendRequest(rawMessageObject);
	}
	
	public void sendDbCharacterReq(int tableId, int characterId, int sessionId) {
		BS2DBCharacterReq.Builder builder = BS2DBCharacterReq.newBuilder();
		builder.setHeadType(HeadType.BS2DB_CHARACTER_REQ);
		builder.setCharId(characterId);
		builder.setTableId(tableId);
		builder.setSessionId(sessionId);
		BS2DBCharacterReq req = builder.build();
		RawMessageObject rawMessageObject = new RawMessageObject((short)HeadType.BS2DB_CHARACTER_REQ_VALUE, req.toByteArray(), session);
		sendRequest(rawMessageObject);
		
	}
	
	public void sendDbBagItemDetailReq(int tableId, int charId, int sessionId,  long uid) 
	{
		BS2DBBagItemDetailReq.Builder builder = BS2DBBagItemDetailReq.newBuilder();
		builder.setHeadType(HeadType.BS2DB_BAG_ITEM_DETAIL_REQ);
		builder.setCharId(charId);
		builder.setTableId(tableId);
		builder.setSessionId(sessionId);
		builder.setUid(uid);
		sendRequest(new RawMessageObject((short)HeadType.BS2DB_BAG_ITEM_DETAIL_REQ_VALUE, 
				builder.build().toByteArray(), session));
	}
	
	public void sendDbDropBagItemReq(int tableId, int charId, int sessionId, List<BagItemChangePb> dropItems) 
	{
		BS2DBDropBagItemReq.Builder builder = BS2DBDropBagItemReq.newBuilder();
		builder.setHeadType(HeadType.BS2DB_DROP_BAG_ITEM_REQ);
		builder.setCharId(charId);
		builder.setTableId(tableId);
		builder.setSessionId(sessionId);
		
		for (BagItemChangePb BagItemChangePb : dropItems) {
			builder.addDropItems(BagItemChangePb);
		}
		sendRequest(new RawMessageObject((short)HeadType.BS2DB_DROP_BAG_ITEM_REQ_VALUE, 
				builder.build().toByteArray(), session));
	}
	
	public void sendDbUseBagItemReq(int tableId, int charId, int sessionId, List<BagItemChangePb> useItems) 
	{
		BS2DBUseBagItemReq.Builder builder = BS2DBUseBagItemReq.newBuilder();
		builder.setHeadType(HeadType.BS2DB_USE_BAG_ITEM_REQ);
		builder.setCharId(charId);
		builder.setTableId(tableId);
		builder.setSessionId(sessionId);
		for (BagItemChangePb BagItemChangePb : useItems) {
			builder.addUseItems(BagItemChangePb);
		}
		sendRequest(new RawMessageObject((short)HeadType.BS2DB_USE_BAG_ITEM_REQ_VALUE, 
				builder.build().toByteArray(), session));
	}
	
	public void sendDbSaveCharacterReq(BS2DBSaveCharacterReq req) {
		sendRequest(new RawMessageObject((short)HeadType.BS2DB_SAVE_CHARACTER_REQ_VALUE, 
				req.toByteArray(), session));
	}
	
	public void sendDbEquipReq(BS2DBEquipReq req) {
		sendRequest(new RawMessageObject((short)HeadType.BS2DB_EQUIP_REQ_VALUE, 
				req.toByteArray(), session));
	}
	
	public void sendDbBuyItemReq(BS2DBBuyItemReq req) {
		sendRequest(new RawMessageObject((short)HeadType.BS2DB_BUY_ITEM_REQ_VALUE, 
				req.toByteArray(), session));
	}
	
	public void sendRequest(RawMessageObject rawMessageObject) {
		this.session.write(rawMessageObject);
	}


	@Override
	public void messageReceived(IoSession session, Object message)
			throws Exception {
		
		RawMessageObject rawMessageObject = (RawMessageObject)message;
		
		//Class cls = Class.forName()
		
		if (rawMessageObject.getHead() == HeadType.DB2BS_TABLE_RESP_VALUE) {
			DB2BSTableResp tableResp = DB2BSTableResp.parseFrom(rawMessageObject.getData());
			String json = tableResp.getJsonData();
			logger.debug("receive table " + tableResp.getTableName() + " size "+tableResp.getSerializedSize());
			//logger.debug(json);
			
			Class genericClass = DatabaseManager.getBeanByTable(tableResp.getTableName());
			String keyString = DatabaseManager.getKeyByTable(tableResp.getTableName());
			try {
				if (genericClass != null) {
					Class[] paramType = new Class[]{String.class, String.class, java.lang.Class.class};
					Class templateClass = TableTemplate.class;
					Constructor[] array = templateClass.getConstructors();
					Constructor constructor = templateClass.getDeclaredConstructor(paramType);
					if (constructor != null) {
						Object object = constructor.newInstance(new Object[] {json, keyString, genericClass});
						if (object instanceof TableTemplate) {
							//logger.debug("settable");
							DatabaseManager.getInstance().setTable((TableTemplate)object);
							if (tableResp.getRowCount() + tableResp.getOffsetRowIndex() >= tableResp.getTotalRowCount()) {
								DatabaseManager.getInstance().setInitTable(tableResp.getTableName(), true);
							}
						}
						else {
							logger.debug("is not instance of table template");
						}
					}
					else {
						logger.debug("constructor not find " + templateClass);
					}
					
				}
				else {
					logger.debug(tableResp.getTableName()+" not exist");
				}
			}
			catch (Exception e){
				e.printStackTrace();
			}
//			if (tableResp.getTableName().equals(DatabaseManager.staticBallTable)) {
//				TableTemplate<BallBean> bean = new TableTemplate<BallBean>(json, "Id", BallBean.class);
//				DatabaseManager.getInstance().setBallTable(bean);
//			}
//			else if (tableResp.getTableName().equals(DatabaseManager.staticLevelExpTable)) {
//				TableTemplate<LevelExpBean> bean = new TableTemplate<LevelExpBean>(json, "Id", LevelExpBean.class);
//				DatabaseManager.getInstance().setLevelExpTable(bean);
//			}
			
    	}
		else if (rawMessageObject.getHead() == HeadType.DB2BS_CHARACTER_RESP_VALUE) {
			logger.debug("receive DB2BS_CHARACTER_RESP_VALUE push to actor");
			DB2BSCharacterResp resp = DB2BSCharacterResp.parseFrom(rawMessageObject.getData());
			MessageObject mo = new MessageObject(rawMessageObject.getHead(), resp, rawMessageObject.getSession());
			MessageCenter.getInstance().dispacthMessage(mo);
			
		}
		else if (rawMessageObject.getHead() == HeadType.DB2BS_DROP_BAG_ITEM_RESP_VALUE) {
			logger.debug("receive DB2BS_DROP_BAG_ITEM_RESP_VALUE push to actor");
			DB2BSDropBagItemResp resp = DB2BSDropBagItemResp.parseFrom(rawMessageObject.getData());
			MessageObject mo = new MessageObject(rawMessageObject.getHead(), resp, rawMessageObject.getSession());
			MessageCenter.getInstance().dispacthMessage(mo);
			
		}
		else if (rawMessageObject.getHead() == HeadType.DB2BS_BUY_ITEM_RESP_VALUE) {
			logger.debug("receive DB2BS_BUY_ITEM_RESP_VALUE push to actor");
			DB2BSBuyItemResp resp = DB2BSBuyItemResp.parseFrom(rawMessageObject.getData());
			MessageObject mo = new MessageObject(rawMessageObject.getHead(), resp, rawMessageObject.getSession());
			MessageCenter.getInstance().dispacthMessage(mo);
			
		}
		else if (rawMessageObject.getHead() == HeadType.DB2BS_EQUIP_RESP_VALUE) {
			logger.debug("receive DB2BS_EQUIP_RESP_VALUE push to actor");
			DB2BSEquipResp resp = DB2BSEquipResp.parseFrom(rawMessageObject.getData());
			MessageObject mo = new MessageObject(rawMessageObject.getHead(), resp, rawMessageObject.getSession());
			MessageCenter.getInstance().dispacthMessage(mo);
		}
		
	}

	@Override
	public void exceptionCaught(IoSession session, Throwable cause)
			throws Exception {
		
	}
}
